
{***********************************************************}
{                                                           }
{      Informix Data Access Components                      }
{                                                           }
{      TIfxCustomDataSet                                    }
{                                                           }
{      Copyright (c) 2001-2008 Luxena Software Company      }
{      http://www.luxena.com                                }
{      e-mail: support@luxena.com                           }
{                                                           }
{***********************************************************}

unit IfxCustomDataSet;

{$I lx.inc}

interface

uses
  Classes, SysUtils, DB, Ifx, LXSQLProcessor, LXDataSetImpl, IfxTypes,
  IfxSqlHdr, IfxSQLProcessor, IfxFieldTypes, IfxConnection, IfxValues, IfxSQL;


const

{ Property Default Values }

  IFX_DEFAULT_REQUEST_LIVE = False;
  IFX_DEFAULT_TRIM_STRINGS = True;
  IFX_DEFAULT_CACHE_BLOBS = True;


type

{ Forward declarations }

  TIfxDataSetSQL = class;
  TIfxRowIDAdapter = class;


{ TIfxCustomDataSet }

  TIfxUpdateApplyEvent = procedure (DataSet: TDataSet; UpdateKind: TUpdateKind;
    Params: TIfxParams) of object;

  TIfxCustomDataSet = class(TDataSet, ILXDataSetSupport, ILXUpdateSQLSupport,
    IIfxConnectionProvider)

  { Construction / Destruction }
  private
    FDataSetImpl: TLXDataSetImpl;
    procedure SetDesignActivation(Value: Boolean);
    function GetDesignActivation: Boolean;
  protected
    procedure Loaded; override;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    property DataSetImpl: TLXDataSetImpl read FDataSetImpl;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    property DesignActivation: Boolean read GetDesignActivation write SetDesignActivation default LX_DEFAULT_DESIGN_ACTIVATION;

  { Records Management }
  private
    procedure SetBufferChunks(Value: Integer);
    function GetBufferChunks: Integer;
    procedure SetPacketRecords(Value: Integer);
    function GetPacketRecords: Integer;
  protected
    procedure InitFieldDefs; override;
    procedure InternalOpen; override;
    procedure InternalClose; override;
    procedure InternalInitFieldDefs; override;
    function IsCursorOpen: Boolean; override;
    function GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
    procedure InternalHandleException; override;
    procedure PrepareCursor; virtual;
    procedure SetMasterParams; virtual; abstract;
  public
    procedure FetchAll;
    procedure Requery;
    property BufferChunks: Integer read GetBufferChunks write SetBufferChunks default LX_DEFAULT_BUFFER_CHUNKS;
    property PacketRecords: Integer read GetPacketRecords write SetPacketRecords default LX_DEFAULT_PACKET_RECORDS;

  { Buffer Management }
  protected
    function AllocRecordBuffer: TRecordBuffer; override;
    procedure FreeRecordBuffer(var Buffer: TRecordBuffer); override;
    procedure InitRecord(Buffer: TRecordBuffer); override;
    procedure ClearCalcFields(Buffer: TRecordBuffer); override;
  public
    function GetCurrentRecord(Buffer: TRecordBuffer): Boolean; override;

  { Field Management }
  protected
    function GetStateFieldValue(State: TDataSetState; Field: TField): Variant; override;
    procedure SetFieldData(Field: TField; Buffer: Pointer); override;
  public
    function FieldDataReadBuffer(Field: TField): PChar;
    function FieldDataWriteBuffer(Field: TField): PChar;
    procedure NotifyFieldChange(Field: TField);
    function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
    function GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean; override;
    function GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer; override;
    function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;

  { Navigation }
  private
    procedure SetUniDirectional(Value: Boolean);
    function GetUniDirectional: Boolean;
  protected
    procedure InternalFirst; override;
    procedure InternalLast; override;
    procedure MoveToRowID(RowID: Integer);
  public
    property UniDirectional: Boolean read GetUniDirectional write SetUniDirectional default LX_DEFAULT_UNIDIRECTIONAL;

  { Editing }
  private
    procedure SetUpdateOptions(Value: TLXRecordUpdateOptions);
    function GetUpdateOptions: TLXRecordUpdateOptions;
    procedure SetUpdateMode(Value: TUpdateMode);
    function GetUpdateMode: TUpdateMode;
    function GetUpdateObject: TLXDataSetUpdateObject;
    procedure SetRefreshLocates(Value: Boolean);
    function GetRefreshLocates: Boolean;
    procedure SetRecordRefreshMode(Value: TLXRecordRefreshMode);
    function GetRecordRefreshMode: TLXRecordRefreshMode;
    procedure SetCachedUpdates(Value: Boolean);
    function GetCachedUpdates: Boolean;
    procedure SetAutoCommitUpdates(Value: Boolean);
    function GetAutoCommitUpdates: Boolean;
  protected
    procedure InternalInitRecord(Buffer: TRecordBuffer); override;
    procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
    procedure InternalInsert; override;
    procedure InternalEdit; override;
    procedure InternalDelete; override;
    procedure InternalPost; override;
    procedure InternalCancel; override;
    procedure InternalRefresh; override;
    procedure SetUpdateObject(Value: TLXDataSetUpdateObject);
  public
    procedure ApplyUpdates; overload;
    procedure ApplyUpdates(CommitUpdates: Boolean); overload;
    procedure CommitUpdates;
    procedure CancelUpdates;
    procedure RevertRecord;
    property UpdateOptions: TLXRecordUpdateOptions read GetUpdateOptions write SetUpdateOptions default LX_DEFAULT_UPDATE_OPTIONS;
    property UpdateMode: TUpdateMode read GetUpdateMode write SetUpdateMode default LX_DEFAULT_UPDATE_MODE;
    property UpdateObject: TLXDataSetUpdateObject read GetUpdateObject;
    property RefreshLocates: Boolean read GetRefreshLocates write SetRefreshLocates default LX_DEFAULT_REFRESH_LOCATES;
    property RecordRefreshMode: TLXRecordRefreshMode read GetRecordRefreshMode write SetRecordRefreshMode default LX_DEFAULT_RECORD_REFRESH_MODE;
    property CachedUpdates: Boolean read GetCachedUpdates write SetCachedUpdates default LX_DEFAULT_CACHED_UPDATES;
    property AutoCommitUpdates: Boolean read GetAutoCommitUpdates write SetAutoCommitUpdates default LX_DEFAULT_AUTO_COMMIT_UPDATES;

  { Informational }
  private
    function GetUpdatesPending: Boolean;
  protected
    function GetCanModify: Boolean; override;
    function GetRecordCount: Longint; override;
    procedure SetRecNo(Value: Integer); override;
    function GetRecNo: Integer; override;
    function GetRecordSize: Word; override;
  public
    function IsSequenced: Boolean; override;
    function UpdateStatus: TUpdateStatus; override;
    property UpdatesPending: Boolean read GetUpdatesPending;

  { Bookmarks Support }
  protected
    procedure GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
    procedure SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer); override;
    function GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag; override;
    procedure SetBookmarkFlag(Buffer: TRecordBuffer; Value: TBookmarkFlag); override;
    procedure InternalGotoBookmark(Bookmark: Pointer); override;
    procedure InternalSetToRecord(Buffer: TRecordBuffer); override;
  public
    function BookmarkValid(Bookmark: TBookmark): Boolean; override;
    function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;

  { Filter / Locate / Find }
  private
    procedure SetStatusFilter(Value: TUpdateStatusSet);
    function GetStatusFilter: TUpdateStatusSet;
  protected
    procedure SetFiltered(Value: Boolean); override;
    procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override;
    procedure FilteredChanged; virtual;
  public
    function Locate(const KeyFields: string; const KeyValues: Variant;
      Options: TLocateOptions): Boolean; override;
    function LocateNext(const KeyFields: string; const KeyValues: Variant;
      Options: TLocateOptions): Boolean;
    function Lookup(const KeyFields: string; const KeyValues: Variant;
      const ResultFields: string): Variant; override;
    property StatusFilter: TUpdateStatusSet read GetStatusFilter write SetStatusFilter;

  { Master / Detail }
  private
    FDataLink: TDataLink;
  protected
    function GetDataSource: TDataSource; override;
    procedure SetDataSource(Value: TDataSource);
    function CreateDataLink: TDataLink; virtual;
    property DataLink: TDataLink read FDataLink;

  { IDAC specific }
  private
    FIfxSQL: TIfxDataSetSQL;
    FFieldTypes: TIfxFieldTypes;
    FRequestLive: Boolean;
    FTrimStrings: Boolean;
    FLobBufferStrategy: TIfxLobBufferStrategy;
    FRowIDAdapter: TIfxRowIDAdapter;
    function GetConnection: TIfxConnection;
    procedure SetConnection(Value: TIfxConnection);
    function GetSQLProcessor: TIfxSQLProcessor;
    procedure SetSQLProcessor(Value: TIfxSQLProcessor);
    function GetActiveSQLProcessor: TIfxSQLProcessor;
    function GetMacros: TStrings;
    procedure SetMacros(Value: TStrings);
    function GetText: string;
    function GetCacheHandle: Boolean;
    procedure SetCacheHandle(Value: Boolean);
    procedure SetRequestLive(Value: Boolean);
    procedure SetFieldTypes(Value: TIfxFieldTypes);
    function GetActiveFieldTypes: TIfxFieldTypes;
    function GetCacheBlobs: Boolean;
    procedure SetCacheBlobs(Value: Boolean);
    function GetParamCheck: Boolean;
    procedure SetParamCheck(Value: Boolean);
    function GetParams: TIfxParams;
    function GetPrepared: Boolean;
    procedure SetPrepared(Value: Boolean);
    function GetHandle: PIfx_Cursor;
    function GetStmtType: Integer;
    function GetRowsAffected: int4;
    function GetResults: TIfxFields;
    procedure SetRowIDAdapter(Value: TIfxRowIDAdapter);
    function GetRowID: int4;
    function GetVersion: string;
    procedure SetVersion(const Value: string);
    procedure ConnectionStateChange(Sender: TObject; Connecting: Boolean);
  protected
    procedure SetName(const NewName: TComponentName); override;
    procedure CheckFieldCompatibility(Field: TField; FieldDef: TFieldDef); override;
    function GetFieldClass(FieldType: TFieldType): TFieldClass; override;
    function CheckConnection: TIfxConnection;
    function CreateInternalSQL(const SQL: string): TIfxSQL;
    property IfxSQL: TIfxDataSetSQL read FIfxSQL;
    property RequestLive: Boolean read FRequestLive write SetRequestLive default IFX_DEFAULT_REQUEST_LIVE;
    property ParamCheck: Boolean read GetParamCheck write SetParamCheck default IFX_DEFAULT_PARAM_CHECK;
    property Params: TIfxParams read GetParams;
    property Prepared: Boolean read GetPrepared write SetPrepared;
    property RowsAffected: int4 read GetRowsAffected;
    property Results: TIfxFields read GetResults;
  public
    property Connection: TIfxConnection read GetConnection write SetConnection;
    property SQLProcessor: TIfxSQLProcessor read GetSQLProcessor write SetSQLProcessor;
    property ActiveSQLProcessor: TIfxSQLProcessor read GetActiveSQLProcessor;
    property Macros: TStrings read GetMacros write SetMacros;
    property Text: string read GetText; 
    property CacheHandle: Boolean read GetCacheHandle write SetCacheHandle default IFX_DEFAULT_CACHE_HANDLE;
    property FieldTypes: TIfxFieldTypes read FFieldTypes write SetFieldTypes;
    property ActiveFieldTypes: TIfxFieldTypes read GetActiveFieldTypes;
    property TrimStrings: Boolean read FTrimStrings write FTrimStrings default IFX_DEFAULT_TRIM_STRINGS;
    property CacheBlobs: Boolean read GetCacheBlobs write SetCacheBlobs default IFX_DEFAULT_CACHE_BLOBS;
    property LobBufferStrategy: TIfxLobBufferStrategy read FLobBufferStrategy;
    property Handle: PIfx_cursor read GetHandle;
    property StmtType: Integer read GetStmtType;
    property RowID: Integer read GetRowID;
    property Version: string read GetVersion write SetVersion stored False;

  { Events }
  private
    FOnUpdateApply: TIfxUpdateApplyEvent;
    FOnRefreshRecord: TIfxUpdateApplyEvent;
    function GetBeforeApplyUpdates: TDataSetNotifyEvent;
    procedure SetBeforeApplyUpdates(Value: TDataSetNotifyEvent);
    function GetAfterApplyUpdates: TDataSetNotifyEvent;
    procedure SetAfterApplyUpdates(Value: TDataSetNotifyEvent);
    function GetOnUpdateRecord: TUpdateRecordEvent;
    procedure SetOnUpdateRecord(Value: TUpdateRecordEvent);
    function GetOnGetTableName: TLXGetTableNameEvent;
    procedure SetOnGetTableName(Value: TLXGetTableNameEvent);
    function GetOnUpdateApplied: TLXUpdateAppliedEvent;
    procedure SetOnUpdateApplied(Value: TLXUpdateAppliedEvent);
    function GetOnUpdateError: TUpdateErrorEvent;
    procedure SetOnUpdateError(Value: TUpdateErrorEvent);
    function GetOnCloseError: TIfxReleaseErrorEvent;
    procedure SetOnCloseError(Value: TIfxReleaseErrorEvent);
    function GetOnUnprepareError: TIfxReleaseErrorEvent;
    procedure SetOnUnprepareError(Value: TIfxReleaseErrorEvent);
  public
    property BeforeApplyUpdates: TDataSetNotifyEvent read GetBeforeApplyUpdates write SetBeforeApplyUpdates;
    property AfterApplyUpdates: TDataSetNotifyEvent read GetAfterApplyUpdates write SetAfterApplyUpdates;
    property OnCloseError: TIfxReleaseErrorEvent read GetOnCloseError write SetOnCloseError;
    property OnUnprepareError: TIfxReleaseErrorEvent read GetOnUnprepareError write SetOnUnprepareError;
    property OnUpdateRecord: TUpdateRecordEvent read GetOnUpdateRecord write SetOnUpdateRecord;
    property OnGetTableName: TLXGetTableNameEvent read GetOnGetTableName write SetOnGetTableName;
    property OnUpdateApply: TIfxUpdateApplyEvent read FOnUpdateApply write FOnUpdateApply;
    property OnUpdateApplied: TLXUpdateAppliedEvent read GetOnUpdateApplied write SetOnUpdateApplied;
    property OnUpdateError: TUpdateErrorEvent read GetOnUpdateError write SetOnUpdateError;
    property OnRefreshRecord: TIfxUpdateApplyEvent read FOnRefreshRecord write FOnRefreshRecord;

  { ILXDataSetSupport }
  protected
    function CreateFieldManager(Field: TField): TLXDataFieldManager;
    function CustomDataSize: Integer;
    function FetchNext: TGetResult;
    function ReadField(Field: TField; Buffer: PChar): Boolean;
    procedure ReadCustomData(Buffer: PChar);
    procedure Restart;

  { ILXUpdateSQLSupport }
  private
    procedure AssignParams(Params: TIfxParams; Delta: TDataSet);
  protected
    function CreateSQLGenerator(Delta: TDataSet): TLXSQLGenerator;
    function ModifyRecord(Delta: TDataSet; UpdateKind: TUpdateKind; const SQL: string) : Integer;
    procedure RefreshRecord(Delta: TDataSet; UpdateKind: TUpdateKind; const SQL: string);

  { IProviderSupport }
  protected
    function PSExecuteStatement(const ASQL: string; AParams: TParams; ResultSet: Pointer = nil): Integer; override;
    function PSGetTableName: string; override;
    function PSGetQuoteChar: string; override;
    function PSGetUpdateException(E: Exception; Prev: EUpdateError): EUpdateError; override;
    function PSIsSQLBased: Boolean; override;
    function PSIsSQLSupported: Boolean; override;
    procedure PSReset; override;
    function PSUpdateRecord(UpdateKind: TUpdateKind; Delta: TDataSet): Boolean; override;
    procedure PSStartTransaction; override;
    procedure PSEndTransaction(Commit: Boolean); override;
    function PSInTransaction: Boolean; override;
  end;


{ TIfxDataSetSQL }

  TIfxDataSetSQL = class(TIfxCustomSQL)
  private
    FOriginalText: string;
    FIgnoreDataEvent: Boolean;
    procedure SetLiveText;
  protected
    function ActiveComponentState: TComponentState; override;
    procedure RegisterConnection; override;
    procedure UnregisterConnection; override;
    procedure ConnectionChanging; override;
    procedure ConnectionClosing; override;
    procedure BuildText; override;
    procedure InternalUnprepare; override;
    procedure UpdateText;
    function IsLiveText: Boolean;
  public
    function DataSet: TIfxCustomDataSet;
    property IgnoreDataEvent: Boolean read FIgnoreDataEvent write FIgnoreDataEvent;
  end;


{ TIfxRowIDAdapter }

  TIfxRowIDAdapter = class
  public
    function DataSize: Integer; virtual;
    function GetRowID: int4; virtual;
    procedure SetRowID(Value: int4); virtual;
  end;

{ TIfxDesignDataSet }

  TIfxDesignDataSet = class(TIfxCustomDataSet)
  published
    { Properties }
    property Active default False;
    property AutoCalcFields;
    property AutoCommitUpdates;
    property BufferChunks;
    property CacheBlobs;
    property CachedUpdates;
    property CacheHandle;
    property Connection;
    property DesignActivation;
    property FieldTypes;
    property Filtered;
    property Macros;
    property ObjectView default False;
    property PacketRecords;
    property RecordRefreshMode;
    property RefreshLocates;
    property SQLProcessor;
    property TrimStrings;
    property UniDirectional;
    property UpdateMode;
    property UpdateOptions;
    property Version;
    { Events }
    property AfterApplyUpdates;
    property AfterCancel;
    property AfterClose;
    property AfterDelete;
    property AfterEdit;
    property AfterInsert;
    property AfterOpen;
    property AfterPost;
    property AfterRefresh;
    property AfterScroll;
    property BeforeApplyUpdates;
    property BeforeCancel;
    property BeforeClose;
    property BeforeDelete;
    property BeforeEdit;
    property BeforeInsert;
    property BeforeOpen;
    property BeforePost;
    property BeforeRefresh;
    property BeforeScroll;
    property OnCalcFields;
    property OnCloseError;
    property OnDeleteError;
    property OnEditError;
    property OnFilterRecord;
    property OnGetTableName;
    property OnNewRecord;
    property OnPostError;
    property OnRefreshRecord;
    property OnUnprepareError;
    property OnUpdateApplied;
    property OnUpdateApply;
    property OnUpdateError;
    property OnUpdateRecord;
  end;


implementation

uses
  DBCommon, LXTypes, LXConsts, LXSysUtils, LXVariants, LXAppUtils,
  LXDBConsts, LXDBTypes, IfxLocator, IfxConsts, IfxConvert, IfxLob,
  IfxNativeFields, IfxQuery, LXIDAC;


type

{ TIfxDetailDataLink }

  TIfxDetailDataLink = class(TDetailDataLink)
  private
    FDataSet: TIfxCustomDataSet;
    procedure RefreshParams;
  protected
    procedure ActiveChanged; override;
    procedure CheckBrowseMode; override;
    procedure LayoutChanged; override;
    procedure RecordChanged(Field: TField); override;
    function GetDetailDataSet: TDataSet; override;
  public
    constructor Create(DataSet: TIfxCustomDataSet);
  end;


{ TIfxHiddenRowIDAdapter }

  TIfxHiddenRowIDAdapter = class(TIfxRowIDAdapter)
  private
    FDataSetImpl: TLXDataSetImpl;
  public
    constructor Create(DataSetImpl: TLXDataSetImpl);
    function DataSize: Integer; override;
    function GetRowID: int4; override;
    procedure SetRowID(Value: int4); override;
  end;


{ TIfxFieldRowIDAdapter }

  TIfxFieldRowIDAdapter = class(TIfxRowIDAdapter)
  private
    FField: TField;
  public
    constructor Create(Field: TField);
    function DataSize: Integer; override;
    function GetRowID: int4; override;
    procedure SetRowID(Value: int4); override;
  end;


{ TIfxLobFieldManager }

  TIfxLobFieldManager = class(TLXDataFieldManager)
  private
    function GetStrategy: TIfxLobBufferStrategy;
  public
    function GetData(RecordBuffer: PLXRecordBuffer; Buffer: PChar): Boolean; override;
    function GetBlobData(RecordBuffer: PLXRecordBuffer): TLXBlobData; override;
    procedure SetData(RecordBuffer: PLXRecordBuffer; Buffer: PChar); override;
    function CreateBlobStream(DataSetImpl: TLXDataSetImpl;
      RecordBuffer: PLXRecordBuffer; Mode: TBlobStreamMode): TStream; override;
    function SetOffset(Value: Integer): Integer; override;
    function RequiresCleanup: Boolean; override;
    procedure Cleanup(RecordBuffer: PLXRecordBuffer); override;
    function CreateReadingStream(RecordBuffer: PLXRecordBuffer): TStream;
    function CreateLob: IIfxLob;
  end;


{ TIfxLobBufferCachedStrategy }

  TIfxLobBufferCachedStrategy = class(TIfxLobBufferStrategy)
  public
    function GetBufferSize: Integer; override;
    procedure Copy(Source, Dest: PChar); override;

    function RequiresCleanup: Boolean; override;
    procedure Cleanup(Buffer: PChar); override;

    function GetAsByte(Buffer: PChar): TBlobByteData; override;
    function GetAsLob(Buffer: PChar): IIfxLob; override;
    function GetAsString(Buffer: PChar): string; override;

    procedure SetAsByte(Buffer: PChar; const Value: TBlobByteData); override;
    procedure SetAsLob(Buffer: PChar; const Value: IIfxLob); override;
    procedure SetAsString(Buffer: PChar; const Value: string); override;

    function CreateReadingStream(Buffer: PChar): TStream; override;
  end;


{ TIfxDataSetLobStream }

  TIfxDataSetLobStream = class(TStream)
  private
    FDataSetImpl: TLXDataSetImpl;
    FFieldManager: TIfxLobFieldManager;
    FRecordBuffer: PLXRecordBuffer;
    FMode: TBlobStreamMode;
    FModified: Boolean;
    FLob: IIfxLob;
    FStream: TStream;
    procedure CheckReadStream;
    procedure CheckWriteStream;
  protected
    procedure SetSize(NewSize: Longint); overload; override;
{$IFDEF D7}
    function GetSize: Int64; override;
{$ENDIF}
{$IFDEF D6}
    procedure SetSize(const NewSize: Int64); overload; override;
{$ENDIF}
  public
    constructor Create(DataSetImpl: TLXDataSetImpl;
      FieldManager: TIfxLobFieldManager; RecordBuffer: PLXRecordBuffer;
      Mode: TBlobStreamMode);
    destructor Destroy; override;
    function Read(var Buffer; Count: Longint): Longint; override;
    function Write(const Buffer; Count: Longint): Longint; override;
    function Seek(Offset: Longint; Origin: Word): Longint; overload; override;
{$IFDEF D6}
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
{$ENDIF}
  end;


{ TIfxDetailDataLink }

constructor TIfxDetailDataLink.Create(DataSet: TIfxCustomDataSet);
begin
  inherited Create;
  FDataSet := DataSet;
end;

procedure TIfxDetailDataLink.ActiveChanged;
begin
  if Active and FDataSet.Active and not (csDestroying in FDataSet.ComponentState) then
    RefreshParams;
end;

procedure TIfxDetailDataLink.CheckBrowseMode;
begin
  if FDataSet.Active then
    FDataSet.CheckBrowseMode;
end;

procedure TIfxDetailDataLink.LayoutChanged;
begin
  ActiveChanged;
end;

procedure TIfxDetailDataLink.RecordChanged(Field: TField);

  function IsMasterChanged: Boolean;
  var
    Params: TIfxParams;
    SourceParams: TParams;
    I: Integer;
    Field: TField;
    Param: TIfxParam;
  begin
    Result := True;

    Params := FDataSet.Params;
    SourceParams := FDataSet.IfxSQL.SourceParams;

    if SourceParams <> nil then
      Params.Assign(SourceParams);
    try
      for I := 0 to Params.Count - 1 do
      begin
        Param := Params[I];
        Field := DataSet.FindField(Param.Name);
        if (Field <> nil) and (Param.Value <> Field.Value) then
          Exit;
      end;
    finally
      if SourceParams <> nil then
        Params.Clear;
    end;

    Result := False;
  end;

begin
  if (Field = nil) and FDataSet.Active and
    (DataSource.State <> dsSetKey) and IsMasterChanged then RefreshParams;
end;

function TIfxDetailDataLink.GetDetailDataSet: TDataSet;
begin
  Result := FDataSet;
end;

procedure TIfxDetailDataLink.RefreshParams;
begin
  FDataSet.SetMasterParams;
  FDataSet.Requery;
end;


{ TIfxRowIDAdapter }

function TIfxRowIDAdapter.DataSize: Integer;
begin
  Result := 0;
end;

function TIfxRowIDAdapter.GetRowID: int4;
begin
  Result := -1;
end;

procedure TIfxRowIDAdapter.SetRowID(Value: int4);
begin
end;


{ TIfxHiddenRowIDAdapter }

constructor TIfxHiddenRowIDAdapter.Create(DataSetImpl: TLXDataSetImpl);
begin
  FDataSetImpl := DataSetImpl;
end;

function TIfxHiddenRowIDAdapter.DataSize: Integer;
begin
  Result := SizeOf(int4);
end;

function TIfxHiddenRowIDAdapter.GetRowID: int4;
var
  P: PInt4;
begin
  P := PInt4(FDataSetImpl.CustomDataBuffer);
  if P <> nil then
    Result := P^ else
    Result := -1;
end;

procedure TIfxHiddenRowIDAdapter.SetRowID(Value: int4);
var
  P: PInt4;
begin
  P := PInt4(FDataSetImpl.CustomDataBuffer);
  if P <> nil then
    P^ := Value;
end;


{ TIfxFieldRowIDAdapter }

constructor TIfxFieldRowIDAdapter.Create(Field: TField);
begin
  FField := Field;
end;

function TIfxFieldRowIDAdapter.DataSize: Integer;
begin
  Result := 0;
end;

function TIfxFieldRowIDAdapter.GetRowID: int4;
begin
  if not FField.IsNull then
    Result := FField.AsInteger else
    Result := -1;
end;

procedure TIfxFieldRowIDAdapter.SetRowID(Value: int4);
begin
  FField.AsInteger := Value;
end;


{ TIfxLobFieldManager }

function TIfxLobFieldManager.GetStrategy: TIfxLobBufferStrategy;
begin
  Result := TIfxCustomDataSet(FField.DataSet).LobBufferStrategy;
end;

function TIfxLobFieldManager.GetData(RecordBuffer: PLXRecordBuffer; Buffer: PChar): Boolean;
var
  P: PChar;
begin
  P := PChar(RecordBuffer) + FOffset;
  Result := P^ <> #0;
  if Result and (Buffer <> nil) then
    IIfxLob(Pointer(Buffer)^) := GetStrategy.GetAsLob(P + 1);
end;

function TIfxLobFieldManager.GetBlobData(RecordBuffer: PLXRecordBuffer): TLXBlobData;
var
  P: PChar;
begin
  P := PChar(RecordBuffer) + FOffset;
  if P^ <> #0 then
    Result := GetStrategy.GetAsString(P + 1) else
    Result := '';
end;

procedure TIfxLobFieldManager.SetData(RecordBuffer: PLXRecordBuffer; Buffer: PChar);
var
  P: PChar;
begin
  P := PChar(RecordBuffer) + FOffset;
  P[-1] := #1;
  P^ := Char(Buffer <> nil);
  if Buffer <> nil then
    GetStrategy.SetAsLob(P + 1, IIfxLob(Pointer(Buffer)^)) else
    GetStrategy.Cleanup(P + 1);
end;

function TIfxLobFieldManager.CreateBlobStream(DataSetImpl: TLXDataSetImpl;
  RecordBuffer: PLXRecordBuffer; Mode: TBlobStreamMode): TStream;
begin
  Result := TIfxDataSetLobStream.Create(DataSetImpl, Self, RecordBuffer, Mode);
end;

function TIfxLobFieldManager.SetOffset(Value: Integer): Integer;
begin
  { Modified Flag  -  Buffer[Offset - 1] }
  { Null Flag      -  Buffer[Offset]     }
  { Data Buffer    -  Buffer[Offset + 1] }
  Inc(Value);
  FOffset := Value;
  Inc(Value);
  Result := Value + GetStrategy.GetBufferSize;
end;

function TIfxLobFieldManager.RequiresCleanup: Boolean;
begin
  Result := GetStrategy.RequiresCleanup;
end;

procedure TIfxLobFieldManager.Cleanup(RecordBuffer: PLXRecordBuffer);
var
  P: PChar;
begin
  P := PChar(RecordBuffer) + FOffset;
  if P^ <> #0 then
    GetStrategy.Cleanup(P + 1);
end;

function TIfxLobFieldManager.CreateReadingStream(RecordBuffer: PLXRecordBuffer): TStream;
var
  P: PChar;
begin
  P := PChar(RecordBuffer) + FOffset;
  if P^ <> #0 then
    Result := GetStrategy.CreateReadingStream(P + 1) else
    Result := nil;
end;

function TIfxLobFieldManager.CreateLob: IIfxLob;
begin
  Result := TIfxLob.Create(TIfxCustomDataSet(FField.DataSet).Connection);
end;


{ TIfxLobBufferCachedStrategy }

function TIfxLobBufferCachedStrategy.GetBufferSize: Integer;
begin
  Result := SizeOf(IIfxLob) + 1 + SizeOf(string);
end;

procedure TIfxLobBufferCachedStrategy.Copy(Source, Dest: PChar);
begin
  IIfxLob(Pointer(Dest)^) := IIfxLob(Pointer(Source)^);
  Inc(Source, SizeOf(IIfxLob));
  Inc(Dest, SizeOf(IIfxLob));
  Dest^ := Source^;
  if Source^ <> #0 then
    PString(Dest + 1)^ := PString(Source + 1)^;
end;

function TIfxLobBufferCachedStrategy.RequiresCleanup: Boolean;
begin
  Result := True;
end;

procedure TIfxLobBufferCachedStrategy.Cleanup(Buffer: PChar);
begin
  SetAsLob(Buffer, nil);
end;

function TIfxLobBufferCachedStrategy.GetAsByte(Buffer: PChar): TBlobByteData;
var
  DataStr: string;
begin
  DataStr := GetAsString(Buffer);
  SetLength(Result, Length(DataStr));
  Move(Pointer(Result)^, PChar(DataStr)^, Length(DataStr));
end;

function TIfxLobBufferCachedStrategy.GetAsLob(Buffer: PChar): IIfxLob;
begin
  Result := IIfxLob(Pointer(Buffer)^);
end;

function TIfxLobBufferCachedStrategy.GetAsString(Buffer: PChar): string;
var
  P: PChar;
begin
  P := Buffer + SizeOf(IIfxLob);
  if P^ <> #0 then
    Result := PString(P + 1)^ else
  begin
    IIfxLob(Pointer(Buffer)^).SaveToBuffer(Result, SetStringSize);
    P^ := #1;
    PString(P + 1)^ := Result;
  end;
end;

procedure TIfxLobBufferCachedStrategy.SetAsByte(Buffer: PChar; const Value: TBlobByteData);
var
  DataStr: string;
begin
  SetString(DataStr, nil, Length(Value));
  Move(PChar(DataStr)^, Pointer(Value)^, Length(Value));
  SetAsString(Buffer, DataStr);
end;

procedure TIfxLobBufferCachedStrategy.SetAsLob(Buffer: PChar; const Value: IIfxLob);
begin
  IIfxLob(Pointer(Buffer)^) := Value;
  Inc(Buffer, SizeOf(IIfxLob));
  Buffer^ := #0;
  PString(Buffer + 1)^ := '';
end;

procedure TIfxLobBufferCachedStrategy.SetAsString(Buffer: PChar; const Value: string);
begin
  inherited;
  Inc(Buffer, SizeOf(IIfxLob));
  Buffer^ := #1;
  PString(Buffer + 1)^ := Value;
end;

function TIfxLobBufferCachedStrategy.CreateReadingStream(Buffer: PChar): TStream;
begin
  Result := TStringStream.Create(GetAsString(Buffer));
end;


{ TIfxDataSetLobStream }

constructor TIfxDataSetLobStream.Create(DataSetImpl: TLXDataSetImpl;
  FieldManager: TIfxLobFieldManager; RecordBuffer: PLXRecordBuffer;
  Mode: TBlobStreamMode);
begin
  FDataSetImpl := DataSetImpl;
  FFieldManager := FieldManager;
  FRecordBuffer := RecordBuffer;
  FMode := Mode;
  FModified := Mode = bmWrite;
end;

destructor TIfxDataSetLobStream.Destroy;
begin
  if FFieldManager = nil then Exit;

  FStream.Free;
  if FLob <> nil then FLob.Close;

  if FModified then
  begin
    if FLob <> nil then
      FFieldManager.SetData(FRecordBuffer, @FLob) else
      FFieldManager.SetData(FRecordBuffer, nil);
    try
      FDataSetImpl.NotifyFieldChange(FFieldManager.Field);
    except
      LXApplicationHandleException(Self);
    end;
  end;
end;

procedure TIfxDataSetLobStream.CheckReadStream;
begin
  if (FStream = nil) and (FRecordBuffer <> nil) then
    if FMode = bmRead then
    begin
      FStream := FFieldManager.CreateReadingStream(FRecordBuffer);
      if FStream = nil then FRecordBuffer := nil;
    end else
    if (FMode = bmReadWrite) and FFieldManager.GetData(FRecordBuffer, @FLob) then
    begin
      FLob.Open(LO_RDWR);
      FStream := TIfxLobStream.Create(FLob);
    end;
end;

procedure TIfxDataSetLobStream.CheckWriteStream;
begin
  if FMode = bmRead then
    IDACErrorFmt(SReadonlyBlobStream, [FFieldManager.Field.FieldName],
      FDataSetImpl.DataSet);
  if (FStream = nil) and (FRecordBuffer <> nil) then
  begin
    if (FMode = bmReadWrite) and FFieldManager.GetData(FRecordBuffer, @FLob) then
      FLob.Open(LO_RDWR) else
      FLob := FFieldManager.CreateLob;
    FStream := TIfxLobStream.Create(FLob);
    FModified := True;
  end;
end;

procedure TIfxDataSetLobStream.SetSize(NewSize: Integer);
begin
  CheckWriteStream;
  if FStream <> nil then
    FStream.Size := NewSize;
end;

{$IFDEF D7}
function TIfxDataSetLobStream.GetSize: Int64;
begin
  CheckReadStream;
  if FStream <> nil then
    Result := FStream.Size else
    Result := 0;
end;
{$ENDIF}

{$IFDEF D6}
procedure TIfxDataSetLobStream.SetSize(const NewSize: Int64);
begin
  CheckWriteStream;
  if FStream <> nil then
    FStream.Size := NewSize;
end;
{$ENDIF}

function TIfxDataSetLobStream.Read(var Buffer; Count: Integer): Longint;
begin
  CheckReadStream;
  if FStream <> nil then
    Result := FStream.Read(Buffer, Count) else
    Result := 0;
end;

function TIfxDataSetLobStream.Write(const Buffer; Count: Integer): Longint;
begin
  CheckWriteStream;
  if FStream <> nil then
    Result := FStream.Write(Buffer, Count) else
    Result := 0;
end;

function TIfxDataSetLobStream.Seek(Offset: Integer; Origin: Word): Longint;
begin
  CheckReadStream;
  if FStream <> nil then
    Result := FStream.Seek(Offset, Origin) else
    Result := 0;
end;

{$IFDEF D6}
function TIfxDataSetLobStream.Seek(const Offset: Int64;
  Origin: TSeekOrigin): Int64;
begin
  CheckReadStream;
  if FStream <> nil then
    Result := FStream.Seek(Offset, Origin) else
    Result := 0;
end;
{$ENDIF}


{ TIfxCustomDataSet }

constructor TIfxCustomDataSet.Create(AOwner: TComponent);
begin
  inherited;
  FDataSetImpl := TLXDataSetImpl.Create(Self, Self, Self);
  FDataLink := CreateDataLink;
  FIfxSQL := TIfxDataSetSQL.Create(Self);
  FRequestLive := IFX_DEFAULT_REQUEST_LIVE;
  FTrimStrings := IFX_DEFAULT_TRIM_STRINGS;
  FLobBufferStrategy := TIfxLobBufferCachedStrategy.Create(Self);
  FRowIDAdapter := TIfxRowIDAdapter.Create;
end;

destructor TIfxCustomDataSet.Destroy;
begin
  Close;
  FRowIDAdapter.Free;
  FLobBufferStrategy.Free;
  FIfxSQL.Free;
  FDataLink.Free;
  SetFieldTypes(nil);
  inherited;
  FDataSetImpl.Free;
end;

procedure TIfxCustomDataSet.SetDesignActivation(Value: Boolean);
begin
  FDataSetImpl.DesignActivation := Value;
end;

function TIfxCustomDataSet.GetDesignActivation: Boolean;
begin
  Result := FDataSetImpl.DesignActivation;
end;

procedure TIfxCustomDataSet.Loaded;
begin
  FIfxSQL.Loaded;
  inherited;
  TIDACEvents.HandleObjectLoaded(Self);
end;

procedure TIfxCustomDataSet.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = FFieldTypes) then
    FFieldTypes := nil;
end;

procedure TIfxCustomDataSet.AfterConstruction;
begin
  inherited;
  TIDACEvents.HandleObjectCreate(Self);
end;

procedure TIfxCustomDataSet.BeforeDestruction;
begin
  TIDACEvents.HandleObjectDestroy(Self);
  inherited;
end;

{ Required for all datasets methods implementation }

procedure TIfxCustomDataSet.SetBufferChunks(Value: Integer);
begin
  FDataSetImpl.BufferChunks := Value;
end;

function TIfxCustomDataSet.GetBufferChunks: Integer;
begin
  Result := FDataSetImpl.BufferChunks;
end;

procedure TIfxCustomDataSet.SetPacketRecords(Value: Integer);
begin
  FDataSetImpl.PacketRecords := Value;
end;

function TIfxCustomDataSet.GetPacketRecords: Integer;
begin
  Result := FDataSetImpl.PacketRecords;
end;

procedure TIfxCustomDataSet.InitFieldDefs;
begin
  InternalInitFieldDefs;
end;

procedure TIfxCustomDataSet.InternalOpen;
begin
  PrepareCursor;
  if FRequestLive and not FIfxSQL.IsLiveText then
    IDACError(SCannotRequestLive, Self);
  if DefaultFields then CreateFields;
  BindFields(True);
  SetRowIDAdapter(TIfxHiddenRowIDAdapter.Create(FDataSetImpl));
  if FDataLink.Active then
    SetMasterParams;
  FIfxSQL.Open;
  FDataSetImpl.InternalOpen;
  FIfxSQL.CountHolder(1);
end;

procedure TIfxCustomDataSet.InternalClose;
begin
  FIfxSQL.CountHolder(-1);
  FDataSetImpl.InternalClose;
  FIfxSQL.Close;
  SetRowIDAdapter(TIfxRowIDAdapter.Create);
  BindFields(False);
  if DefaultFields then DestroyFields;
end;

procedure TIfxCustomDataSet.InternalInitFieldDefs;
var
  I: Integer;
  FieldCount: Integer;
  ColSize: Integer;
  FieldDef: TFieldDef;
begin
  FIfxSQL.Prepare;

  FieldDefs.BeginUpdate;
  try
    FieldDefs.Clear;
    FieldCount := FIfxSQL.FieldCount;
    if FRequestLive and not FieldDefs.HiddenFields then
      Dec(FieldCount);
    FieldDef := nil;
    for I := 0 to FieldCount - 1 do
      with FIfxSQL.Fields[I] do
      begin
        ColSize := Size;
        if ActiveFieldTypes.IsStringType(DataType, ExtendedType) then Dec(ColSize);
        FieldDef := FieldDefs.AddFieldDef;
        ActiveFieldTypes.InitFieldDef(FieldDef, FieldNoOfs + I, Name, DataType,
          ColSize, ExtendedType);
      end;
    if FRequestLive and FieldDefs.HiddenFields and (FieldDef <> nil) then
      FieldDef.Attributes := [faHiddenCol, faReadonly];
  finally
    FieldDefs.EndUpdate;
  end;
end;

function TIfxCustomDataSet.IsCursorOpen: Boolean;
begin
  Result := FDataSetImpl.IsCursorOpen;
end;

function TIfxCustomDataSet.GetRecord(Buffer: TRecordBuffer; GetMode: TGetMode;
  DoCheck: Boolean): TGetResult;
begin
  CheckLicense;
  Result := FDataSetImpl.GetRecord(Buffer, GetMode, DoCheck);
end;

procedure TIfxCustomDataSet.InternalHandleException;
begin
  LXApplicationHandleException(Self);
end;

procedure TIfxCustomDataSet.PrepareCursor;
begin
  FieldDefs.Updated := False;
  FieldDefs.Update;
end;

procedure TIfxCustomDataSet.FetchAll;
begin
  FDataSetImpl.FetchAll;
end;

procedure TIfxCustomDataSet.Requery;
begin
  FDataSetImpl.Requery;
end;

{ Buffer Management }

function TIfxCustomDataSet.AllocRecordBuffer: TRecordBuffer;
begin
  Result := FDataSetImpl.AllocRecordBuffer;
end;

procedure TIfxCustomDataSet.FreeRecordBuffer(var Buffer: TRecordBuffer);
begin
  FDataSetImpl.FreeRecordBuffer(Buffer);
end;

procedure TIfxCustomDataSet.InitRecord(Buffer: TRecordBuffer);
begin
  FDataSetImpl.InitRecord(Buffer);
end;

procedure TIfxCustomDataSet.ClearCalcFields(Buffer: TRecordBuffer);
begin
  FDataSetImpl.ClearCalcFields(Buffer);
end;

function TIfxCustomDataSet.GetCurrentRecord(Buffer: TRecordBuffer): Boolean;
begin
  Result := FDataSetImpl.GetCurrentRecord(Buffer);
end;

{ Field Management }

function TIfxCustomDataSet.GetStateFieldValue(State: TDataSetState; Field: TField): Variant;
begin
  if FDataSetImpl.HasFieldStateValue(State, Field) then
    Result := inherited GetStateFieldValue(State, Field) else
    Result := LXUnassigned;
end;

procedure TIfxCustomDataSet.SetFieldData(Field: TField; Buffer: Pointer);
begin
  FDataSetImpl.SetFieldData(Field, Buffer);
end;

function TIfxCustomDataSet.FieldDataReadBuffer(Field: TField): PChar;
begin
  Result := FDataSetImpl.FieldDataReadBuffer(Field);
end;

function TIfxCustomDataSet.FieldDataWriteBuffer(Field: TField): PChar;
begin
  Result := FDataSetImpl.FieldDataWriteBuffer(Field);
end;

procedure TIfxCustomDataSet.NotifyFieldChange(Field: TField);
begin
  FDataSetImpl.NotifyFieldChange(Field);
end;

function TIfxCustomDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
begin
  Result := FDataSetImpl.GetFieldData(Field, Buffer);
end;

function TIfxCustomDataSet.GetFieldData(FieldNo: Integer; Buffer: Pointer): Boolean;
begin
  Result := FDataSetImpl.GetFieldData(FieldNo, Buffer);
end;

function TIfxCustomDataSet.GetBlobFieldData(FieldNo: Integer; var Buffer: TBlobByteData): Integer;
begin
  Result := FDataSetImpl.GetBlobFieldData(FieldNo, Buffer);
end;

function TIfxCustomDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
begin
  Result := FDataSetImpl.CreateBlobStream(Field, Mode);
end;

{ Navigation }

procedure TIfxCustomDataSet.SetUniDirectional(Value: Boolean);
begin
  FDataSetImpl.UniDirectional := Value;
end;

function TIfxCustomDataSet.GetUniDirectional: Boolean;
begin
  Result := FDataSetImpl.UniDirectional;
end;

procedure TIfxCustomDataSet.InternalFirst;
begin
  FDataSetImpl.InternalFirst;
end;

procedure TIfxCustomDataSet.InternalLast;
begin
  FDataSetImpl.InternalLast;
end;

procedure TIfxCustomDataSet.MoveToRowID(RowID: Integer);
begin
  SetTempState(dsFilter);
  try
    while FDataSetImpl.GetRecord(gmNext) = grOK do
    begin
      FDataSetImpl.SetCurrentAsFiltered;
      if Self.RowID = RowID then Exit;
    end;
    FDataSetImpl.InternalFirst;
  finally
    RestoreState(dsBrowse);
  end;
end;

{ Editing }

procedure TIfxCustomDataSet.SetUpdateOptions(Value: TLXRecordUpdateOptions);
begin
  FDataSetImpl.UpdateOptions := Value;
end;

function TIfxCustomDataSet.GetUpdateOptions: TLXRecordUpdateOptions;
begin
  Result := FDataSetImpl.UpdateOptions;
end;

procedure TIfxCustomDataSet.SetUpdateMode(Value: TUpdateMode);
begin
  FDataSetImpl.UpdateMode := Value;
end;

function TIfxCustomDataSet.GetUpdateMode: TUpdateMode;
begin
  Result := FDataSetImpl.UpdateMode;
end;

function TIfxCustomDataSet.GetUpdateObject: TLXDataSetUpdateObject;
begin
  Result := FDataSetImpl.UpdateObject;
end;

procedure TIfxCustomDataSet.SetRefreshLocates(Value: Boolean);
begin
  FDataSetImpl.RefreshLocates := Value;
end;

function TIfxCustomDataSet.GetRefreshLocates: Boolean;
begin
  Result := FDataSetImpl.RefreshLocates;
end;

procedure TIfxCustomDataSet.SetRecordRefreshMode(Value: TLXRecordRefreshMode);
begin
  FDataSetImpl.RecordRefreshMode := Value;
end;

function TIfxCustomDataSet.GetRecordRefreshMode: TLXRecordRefreshMode;
begin
  Result := FDataSetImpl.RecordRefreshMode;
end;

procedure TIfxCustomDataSet.SetCachedUpdates(Value: Boolean);
begin
  FDataSetImpl.CachedUpdates := Value;
end;

function TIfxCustomDataSet.GetCachedUpdates: Boolean;
begin
  Result := FDataSetImpl.CachedUpdates;
end;

procedure TIfxCustomDataSet.SetAutoCommitUpdates(Value: Boolean);
begin
  FDataSetImpl.AutoCommitUpdates := Value;
end;

function TIfxCustomDataSet.GetAutoCommitUpdates: Boolean;
begin
  Result := FDataSetImpl.AutoCommitUpdates;
end;

procedure TIfxCustomDataSet.InternalInitRecord(Buffer: TRecordBuffer);
begin
  FDataSetImpl.InternalInitRecord(Buffer);
end;

procedure TIfxCustomDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
begin
  FDataSetImpl.InternalAddRecord(Buffer, Append);
end;

procedure TIfxCustomDataSet.InternalInsert;
begin
  FDataSetImpl.InternalInsert;
end;

procedure TIfxCustomDataSet.InternalEdit;
begin
  FDataSetImpl.InternalEdit;
end;

procedure TIfxCustomDataSet.InternalDelete;
begin
  FDataSetImpl.InternalDelete;
end;

procedure TIfxCustomDataSet.InternalPost;
begin
  FDataSetImpl.InternalPost;
end;

procedure TIfxCustomDataSet.InternalCancel;
begin
  FDataSetImpl.InternalCancel;
end;

procedure TIfxCustomDataSet.InternalRefresh;
var
  RowID: Integer;
begin
  if RefreshLocates then
    RowID := Self.RowID else
    RowID := -1;
  if RowID < 0 then
    FDataSetImpl.InternalRefresh else
  begin
    FDataSetImpl.InternalRestart;
    MoveToRowID(RowID);
  end;
end;

procedure TIfxCustomDataSet.SetUpdateObject(Value: TLXDataSetUpdateObject);
begin
  FDataSetImpl.UpdateObject := Value;
end;

procedure TIfxCustomDataSet.ApplyUpdates;
begin
  FDataSetImpl.ApplyUpdates;
end;

procedure TIfxCustomDataSet.ApplyUpdates(CommitUpdates: Boolean);
begin
  FDataSetImpl.ApplyUpdates(CommitUpdates);
end;

procedure TIfxCustomDataSet.CommitUpdates;
begin
  FDataSetImpl.CommitUpdates;
end;

procedure TIfxCustomDataSet.CancelUpdates;
begin
  FDataSetImpl.CancelUpdates;
end;

procedure TIfxCustomDataSet.RevertRecord;
begin
  FDataSetImpl.RevertRecord;
end;

{ Informational }

function TIfxCustomDataSet.GetUpdatesPending: Boolean;
begin
  Result := FDataSetImpl.UpdatesPending;
end;

function TIfxCustomDataSet.GetCanModify: Boolean;
begin
  Result := FDataSetImpl.GetCanModify;
end;

function TIfxCustomDataSet.GetRecordCount: Longint;
begin
  Result := FDataSetImpl.GetRecordCount;
end;

procedure TIfxCustomDataSet.SetRecNo(Value: Integer);
begin
  FDataSetImpl.SetRecNo(Value);
end;

function TIfxCustomDataSet.GetRecNo: Integer;
begin
  Result := FDataSetImpl.GetRecNo;
end;

function TIfxCustomDataSet.GetRecordSize: Word;
begin
  Result := FDataSetImpl.GetRecordSize;
end;

function TIfxCustomDataSet.IsSequenced: Boolean;
begin
  Result := FDataSetImpl.IsSequenced;
end;

function TIfxCustomDataSet.UpdateStatus: TUpdateStatus;
begin
  Result := FDataSetImpl.UpdateStatus;
end;

{ Bookmarks Support }

procedure TIfxCustomDataSet.GetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
begin
  FDataSetImpl.GetBookmarkData(Buffer, Data);
end;

procedure TIfxCustomDataSet.SetBookmarkData(Buffer: TRecordBuffer; Data: Pointer);
begin
  FDataSetImpl.SetBookmarkData(Buffer, Data);
end;

function TIfxCustomDataSet.GetBookmarkFlag(Buffer: TRecordBuffer): TBookmarkFlag;
begin
  Result := FDataSetImpl.GetBookmarkFlag(Buffer);
end;

procedure TIfxCustomDataSet.SetBookmarkFlag(Buffer: TRecordBuffer;
  Value: TBookmarkFlag);
begin
  FDataSetImpl.SetBookmarkFlag(Buffer, Value);
end;

procedure TIfxCustomDataSet.InternalGotoBookmark(Bookmark: Pointer);
begin
  FDataSetImpl.InternalGotoBookmark(Bookmark);
end;

procedure TIfxCustomDataSet.InternalSetToRecord(Buffer: TRecordBuffer);
begin
  FDataSetImpl.InternalSetToRecord(Buffer);
end;

function TIfxCustomDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
begin
  Result := FDataSetImpl.BookmarkValid(Bookmark);
end;

function TIfxCustomDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
begin
  Result := FDataSetImpl.CompareBookmarks(Bookmark1, Bookmark2);
end;

{ Filter / Locate / Find }

procedure TIfxCustomDataSet.SetStatusFilter(Value: TUpdateStatusSet);
begin
  FDataSetImpl.StatusFilter := Value;
end;

function TIfxCustomDataSet.GetStatusFilter: TUpdateStatusSet;
begin
  Result := FDataSetImpl.StatusFilter;
end;

procedure TIfxCustomDataSet.SetFiltered(Value: Boolean);
begin
  if Filtered <> Value then
  begin
    inherited SetFiltered(Value);
    FilteredChanged;
  end;
end;

procedure TIfxCustomDataSet.SetOnFilterRecord(const Value: TFilterRecordEvent);
begin
  if @OnFilterRecord <> @Value then
  begin
    inherited SetOnFilterRecord(Value);
    if Active and Filtered then First;
  end;
end;

procedure TIfxCustomDataSet.FilteredChanged;
begin
  if Active and Assigned(OnFilterRecord) then First;
end;

function TIfxCustomDataSet.Locate(const KeyFields: string;
  const KeyValues: Variant; Options: TLocateOptions): Boolean;
begin
  Result := FDataSetImpl.Locate(KeyFields, KeyValues, Options, True);
end;

function TIfxCustomDataSet.LocateNext(const KeyFields: string;
  const KeyValues: Variant; Options: TLocateOptions): Boolean;
begin
  Result := FDataSetImpl.Locate(KeyFields, KeyValues, Options, False);
end;

function TIfxCustomDataSet.Lookup(const KeyFields: string;
  const KeyValues: Variant; const ResultFields: string): Variant;
begin
  Result := FDataSetImpl.Lookup(KeyFields, KeyValues, ResultFields);
end;

{ Master / Detail }

function TIfxCustomDataSet.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

procedure TIfxCustomDataSet.SetDataSource(Value: TDataSource);
begin
  if IsLinkedTo(Value) then
    IDACError(SCircularDataLink, Self);
  FDataLink.DataSource := Value;
end;

function TIfxCustomDataSet.CreateDataLink: TDataLink;
begin
  Result := TIfxDetailDataLink.Create(Self);
end;

{ IDAC specific }

function TIfxCustomDataSet.GetConnection: TIfxConnection;
begin
  Result := FIfxSQL.Connection;
end;

procedure TIfxCustomDataSet.SetConnection(Value: TIfxConnection);
begin
  FIfxSQL.Connection := Value;
end;

function TIfxCustomDataSet.GetSQLProcessor: TIfxSQLProcessor;
begin
  Result := FIfxSQL.SQLProcessor;
end;

procedure TIfxCustomDataSet.SetSQLProcessor(Value: TIfxSQLProcessor);
begin
  FIfxSQL.SQLProcessor := Value;
end;

function TIfxCustomDataSet.GetActiveSQLProcessor: TIfxSQLProcessor;
begin
  Result := FIfxSQL.ActiveSQLProcessor;
end;

function TIfxCustomDataSet.GetMacros: TStrings;
begin
  Result := FIfxSQL.Macros;
end;

procedure TIfxCustomDataSet.SetMacros(Value: TStrings);
begin
  FIfxSQL.Macros := Value;
end;

function TIfxCustomDataSet.GetText: string;
begin
  Result := FIfxSQL.Text;
end;

function TIfxCustomDataSet.GetCacheHandle: Boolean;
begin
  Result := FIfxSQL.CacheHandle;
end;

procedure TIfxCustomDataSet.SetCacheHandle(Value: Boolean);
begin
  FIfxSQL.CacheHandle := Value;
end;

procedure TIfxCustomDataSet.SetRequestLive(Value: Boolean);
begin
  if Value <> FRequestLive then
    if csReading in ComponentState then
      FRequestLive := Value else
    begin
      CheckInactive;
      FRequestLive := Value;
      FIfxSQL.UpdateText;
    end;
end;

procedure TIfxCustomDataSet.SetFieldTypes(Value: TIfxFieldTypes);
begin
  if Value <> FFieldTypes then
  begin
    if FFieldTypes <> nil then
      FFieldTypes.RemoveFreeNotification(Self);
    FFieldTypes := Value;
    if FFieldTypes <> nil then
      FFieldTypes.FreeNotification(Self);
  end;
end;

function TIfxCustomDataSet.GetActiveFieldTypes: TIfxFieldTypes;
begin
  Result := FFieldTypes;
  if Result = nil then
    if Connection <> nil then
      Result := Connection.ActiveFieldTypes else
      Result := TIfxFieldTypes.DefaultTypes;
end;

function TIfxCustomDataSet.GetCacheBlobs: Boolean;
begin
  Result := FLobBufferStrategy is TIfxLobBufferCachedStrategy;
end;

procedure TIfxCustomDataSet.SetCacheBlobs(Value: Boolean);
begin
  CheckInactive;
  if Value <> CacheBlobs then
  begin
    FLobBufferStrategy.Free;
    if Value then
      FLobBufferStrategy := TIfxLobBufferCachedStrategy.Create(Self) else
      FLobBufferStrategy := TIfxLobBufferStrategy.Create(Self);
  end;
end;

function TIfxCustomDataSet.GetParamCheck: Boolean;
begin
  Result := FIfxSQL.ParamCheck;
end;

procedure TIfxCustomDataSet.SetParamCheck(Value: Boolean);
begin
  FIfxSQL.ParamCheck := Value;
end;

function TIfxCustomDataSet.GetParams: TIfxParams;
begin
  Result := FIfxSQL.Params;
end;

function TIfxCustomDataSet.GetPrepared: Boolean;
begin
  Result := FIfxSQL.Prepared;
end;

procedure TIfxCustomDataSet.SetPrepared(Value: Boolean);
begin
  FIfxSQL.Prepared := Value;
end;

function TIfxCustomDataSet.GetHandle: PIfx_Cursor;
begin
  Result := FIfxSQL.Handle;
end;

function TIfxCustomDataSet.GetStmtType: Integer;
begin
  Result := FIfxSQL.StmtType;
end;

function TIfxCustomDataSet.GetRowsAffected: int4;
begin
  Result := FIfxSQL.RowsAffected;
end;

function TIfxCustomDataSet.GetResults: TIfxFields;
begin
  Result := FIfxSQL.Fields;
end;

procedure TIfxCustomDataSet.SetRowIDAdapter(Value: TIfxRowIDAdapter);
begin
  FRowIDAdapter.Free;
  FRowIDAdapter := Value;
end;

function TIfxCustomDataSet.GetRowID: int4;
begin
  Result := FRowIDAdapter.GetRowID;
end;

function TIfxCustomDataSet.GetVersion: string;
begin
  Result := IDACVersion;
end;

procedure TIfxCustomDataSet.SetVersion(const Value: string);
begin
end;

procedure TIfxCustomDataSet.ConnectionStateChange(Sender: TObject; Connecting: Boolean);
begin
  if not Connecting then
    FIfxSQL.ConnectionClosing;
end;

procedure TIfxCustomDataSet.SetName(const NewName: TComponentName);
begin
  inherited;
  FIfxSQL.Name := NewName;
end;

procedure TIfxCustomDataSet.CheckFieldCompatibility(Field: TField;
  FieldDef: TFieldDef);
begin
  { IDAC performs automatic data type convertion }
end;

function TIfxCustomDataSet.GetFieldClass(FieldType: TFieldType): TFieldClass;
begin
  Result := nil;

  with ActiveFieldTypes do
    case FieldType of
      ftString, ftWideString: if NativeCharacter then Result := TIfxVarCharField;
      ftSmallint: if NativeNumeric then Result := TIfxSmallIntField;
      ftInteger: if NativeNumeric then Result := TIfxIntegerField;
      ftFloat: if NativeNumeric then Result := TIfxFloatField;
      ftCurrency: if NativeNumeric then Result := TIfxMoneyField;
      ftBCD{$IFDEF D6}, ftFMTBcd{$ENDIF}: if NativeNumeric then Result := TIfxDecimalField;
      ftDate: if NativeTime then Result := TIfxDateField;
      ftDateTime: if NativeTime then Result := TIfxDateTimeField;
      ftAutoInc: if NativeNumeric then Result := TIfxSerialField;
      ftLargeint: if NativeNumeric then Result := TIfxInt8Field;
      ftFixedChar: if NativeCharacter then Result := TIfxCharField;
      ftOraBlob: if NativeSmartLob then Result := TIfxBlobField;
      ftOraClob: if NativeSmartLob then Result := TIfxClobField;
{$IFDEF D6}
      ftTimeStamp: if NativeTime then Result := TIfxDateTimeField;
{$ENDIF}
    end;

  if Result = nil then
    Result := inherited GetFieldClass(FieldType);
end;

function TIfxCustomDataSet.CheckConnection: TIfxConnection;
begin
  Result := FIfxSQL.CheckConnection;
end;

function TIfxCustomDataSet.CreateInternalSQL(const SQL: string): TIfxSQL;
begin
  Result := TIfxSQL.Create(nil);
  try
    Result.Name := Name;
    Result.Connection := Connection;
    Result.SQLProcessor := ActiveSQLProcessor;
    Result.SQL.Text := SQL;
  except
    Result.Free;
    raise;
  end;
end;

{ Events }

function TIfxCustomDataSet.GetBeforeApplyUpdates: TDataSetNotifyEvent;
begin
  Result := FDataSetImpl.BeforeApplyUpdates;
end;

procedure TIfxCustomDataSet.SetBeforeApplyUpdates(Value: TDataSetNotifyEvent);
begin
  FDataSetImpl.BeforeApplyUpdates := Value;
end;

function TIfxCustomDataSet.GetAfterApplyUpdates: TDataSetNotifyEvent;
begin
  Result := FDataSetImpl.AfterApplyUpdates;
end;

procedure TIfxCustomDataSet.SetAfterApplyUpdates(Value: TDataSetNotifyEvent);
begin
  FDataSetImpl.AfterApplyUpdates := Value;
end;

function TIfxCustomDataSet.GetOnUpdateRecord: TUpdateRecordEvent;
begin
  Result := FDataSetImpl.OnUpdateRecord;
end;

procedure TIfxCustomDataSet.SetOnUpdateRecord(Value: TUpdateRecordEvent);
begin
  FDataSetImpl.OnUpdateRecord := Value;
end;

function TIfxCustomDataSet.GetOnGetTableName: TLXGetTableNameEvent;
begin
  Result := FDataSetImpl.OnGetTableName;
end;

procedure TIfxCustomDataSet.SetOnGetTableName(Value: TLXGetTableNameEvent);
begin
  FDataSetImpl.OnGetTableName := Value;
end;

function TIfxCustomDataSet.GetOnUpdateApplied: TLXUpdateAppliedEvent;
begin
  Result := FDataSetImpl.OnUpdateApplied;
end;

procedure TIfxCustomDataSet.SetOnUpdateApplied(Value: TLXUpdateAppliedEvent);
begin
  FDataSetImpl.OnUpdateApplied := Value;
end;

function TIfxCustomDataSet.GetOnUpdateError: TUpdateErrorEvent;
begin
  Result := FDataSetImpl.OnUpdateError;
end;

procedure TIfxCustomDataSet.SetOnUpdateError(Value: TUpdateErrorEvent);
begin
  FDataSetImpl.OnUpdateError := Value;
end;

function TIfxCustomDataSet.GetOnCloseError: TIfxReleaseErrorEvent;
begin
  Result := FIfxSQL.OnCloseError;
end;

procedure TIfxCustomDataSet.SetOnCloseError(Value: TIfxReleaseErrorEvent);
begin
  FIfxSQL.OnCloseError := Value;
end;

function TIfxCustomDataSet.GetOnUnprepareError: TIfxReleaseErrorEvent;
begin
  Result := FIfxSQL.OnUnprepareError;
end;

procedure TIfxCustomDataSet.SetOnUnprepareError(Value: TIfxReleaseErrorEvent);
begin
  FIfxSQL.OnUnprepareError := Value;
end;

{ TIfxCustomDataSet.ILXDataSetSupport }

function TIfxCustomDataSet.CreateFieldManager(Field: TField): TLXDataFieldManager;
begin
  if Field.IsBlob then
    if Field.DataType in [ftOraBlob, ftOraClob] then
      Result := TIfxLobFieldManager.Create(Field) else
      Result := TLXBlobFieldManager.Create(Field)
  else
    Result := TLXDataFieldManager.Create(Field);

  if FRequestLive and (Field.FieldNo - FieldNoOfs = FIfxSQL.FieldCount - 1) then
    SetRowIDAdapter(TIfxFieldRowIDAdapter.Create(Field));
end;

function TIfxCustomDataSet.CustomDataSize: Integer;
begin
  Result := FRowIDAdapter.DataSize;
end;

function TIfxCustomDataSet.FetchNext: TGetResult;
begin
  FIfxSQL.Next;
  if not FIfxSQL.EOF then
    Result := grOK else
  begin
    FIfxSQL.Close;
    Result := grEOF;
  end;
end;

function TIfxCustomDataSet.ReadField(Field: TField; Buffer: PChar): Boolean;
var
  IfxField: TIfxField;
begin
  IfxField := FIfxSQL.Fields[Field.FieldNo - FieldNoOfs];

  Result := not IfxField.IsNull;
  if not Result then Exit;

  if Field is TIfxNativeField then
    TIfxNativeField(Field).CopyValue(IfxField, Buffer^)
  else if Field.IsBlob then
    if Field.DataType in [ftOraBlob, ftOraClob] then
      FLobBufferStrategy.SetAsLob(Buffer, FIfxSQL.Fields[Field.FieldNo - 1].AsLob) else
      PLXBlobData(Buffer)^ := FIfxSQL.Fields[Field.FieldNo - 1].AsString
  else
    case Field.DataType of
      ftString, ftFixedChar:
        if FTrimStrings then
          LXStrPLCopy(Buffer, TrimRight(IfxField.AsString), Field.Size) else
          LXStrPLCopy(Buffer, IfxField.AsString, Field.Size);
      ftSmallint:
        PSmallInt(Buffer)^ := IfxField.AsSmallInt;
      ftInteger, ftAutoInc:
        PInteger(Buffer)^ := IfxField.AsInteger;
      ftBoolean:
        PWordBool(Buffer)^ := IfxField.AsBoolean;
      ftFloat:
        PDouble(Buffer)^ := IfxField.AsFloat;
      ftCurrency:
        PDouble(Buffer)^ := IfxField.AsFloat;
      ftBCD{$IFDEF D6}, ftFMTBcd{$ENDIF}:
        PBcd(Buffer)^ := IfxField.AsBCD;
      ftDate:
        PInteger(Buffer)^ := IfxField.AsDate + IFX_DATE_DELTA;
      ftTime:
        PInteger(Buffer)^ := DateTimeToTimeStamp(IfxField.AsDateTime).Time;
      ftDateTime:
        PDouble(Buffer)^ := TimeStampToMSecs(DateTimeToTimeStamp(IfxField.AsDateTime));
      ftLargeint:
        PInt64(Buffer)^ := IfxField.AsLargeint;
{$IFDEF D6}
      ftTimeStamp:
        PSQLTimeStamp(Buffer)^ := IfxField.AsSQLTimeStamp;
{$ENDIF}
      else
        IDACErrorFmt(SUnsupportedFieldType, [FieldTypeNames[Field.DataType],
          Field.FieldName], Self);
    end;
end;

procedure TIfxCustomDataSet.ReadCustomData(Buffer: PChar);
begin
  PInt4(Buffer)^ := PInt4(FIfxSQL.Fields[FIfxSQL.FieldCount - 1].Data^.sqldata)^;
end;

procedure TIfxCustomDataSet.Restart;
begin
  FIfxSQL.Close;
  FIfxSQL.Open;
end;

{ TIfxCustomDataSet.ILXUpdateSQLSupport }

function TIfxCustomDataSet.CreateSQLGenerator(Delta: TDataSet): TLXSQLGenerator;
begin
  Result := ActiveSQLProcessor.CreateSQLGenerator(True, '', PSGetTableName,
    Delta.Fields);
  if (Delta = Self) and FRequestLive then
    Result.CustomFilter := 'rowid = ' + Result.GetParamStr('', 'rowid');
  Result.UpdateMode := UpdateMode;
end;

procedure TIfxCustomDataSet.AssignParams(Params: TIfxParams; Delta: TDataSet);
var
  Param: TIfxParam;
  FieldName: string;
  Field: TField;
  IsOld: Boolean;
  I: Integer;
  StateContext: TLXDataSetStateContext;
begin
  for I := 0 to Params.Count - 1 do
  begin
    Param := Params[I];
    if Param.Modified then Continue; 

    FieldName := Param.Name;

    if (Delta = Self) and FRequestLive and (LowerCase(FieldName) = 'rowid') then
    begin
      StateContext := FDataSetImpl.SetDataSetState(Delta, dsNewValue);
      try
        Param.AsInteger := FRowIDAdapter.GetRowID;
      finally
        FDataSetImpl.RestoreDataSetState(Delta, StateContext);
      end;
    end else
    begin
      IsOld := StrLComp(PChar(FieldName), 'OLD_', 4) = 0;
      if IsOld then System.Delete(FieldName, 1, 4);

      Field := Delta.FindField(FieldName);
      if Field = nil then Continue;

      if IsOld or LXVarIsClear(Field.NewValue) then
        StateContext := FDataSetImpl.SetDataSetState(Delta, dsOldValue) else
        StateContext := FDataSetImpl.SetDataSetState(Delta, dsNewValue);
      try
        Param.AssignField(Field);
      finally
        FDataSetImpl.RestoreDataSetState(Delta, StateContext);
      end;
    end;
  end;
end;

function TIfxCustomDataSet.ModifyRecord(Delta: TDataSet; UpdateKind: TUpdateKind;
  const SQL: string): Integer;
var
  IfxSQL: TIfxSQL;
  StateContext: TLXDataSetStateContext;
  I: Integer;
begin
  IfxSQL := CreateInternalSQL(SQL);
  try
    if Assigned(FOnUpdateApply) then
      FOnUpdateApply(Delta, UpdateKind, IfxSQL.Params);
    AssignParams(IfxSQL.Params, Delta);
    Result := IfxSQL.Execute;
  finally
    IfxSQL.Free;
  end;
  if UpdateKind = ukInsert then
  begin
    StateContext := FDataSetImpl.SetDataSetState(Delta, dsNewValue);
    try
      for I := 0 to Delta.FieldCount - 1 do
        with Delta.Fields[I] do
          if AutoGenerateValue = arAutoInc then
            case DataType of
              ftInteger, ftAutoInc:
                AsInteger := Connection.LastInsertedSerial;
              ftLargeint:
                AsString := Int8ToString(Connection.LastInsertedSerial8);
            end;
      if (Delta = Self) and FRequestLive then
        FRowIDAdapter.SetRowID(Connection.LastInsertedRowID);
    finally
      FDataSetImpl.RestoreDataSetState(Delta, StateContext);
    end;
  end;
end;

procedure TIfxCustomDataSet.RefreshRecord(Delta: TDataSet;
  UpdateKind: TUpdateKind; const SQL: string);
var
  IfxSQL: TIfxSQL;
  StateContext: TLXDataSetStateContext;
  I: Integer;
  SourceField: TIfxField;
  DestField: TField;
begin
  try
    IfxSQL := CreateInternalSQL(SQL);
    try
      if Assigned(FOnRefreshRecord) then
        FOnRefreshRecord(Delta, UpdateKind, IfxSQL.Params);
      AssignParams(IfxSQL.Params, Delta);
      if IfxSQL.Execute <> 0 then
      begin
        StateContext := FDataSetImpl.SetDataSetState(Delta, dsNewValue);
        try
          for I := 0 to FieldCount - 1 do
          begin
            SourceField := IfxSQL.Fields[I];
            DestField := Delta.FindField(SourceField.Name);
            if DestField <> nil then
            begin
              SourceField.AssignToField(DestField);
              if (DestField.DataType in [ftString, ftFixedChar]) and FTrimStrings then
                DestField.AsString := TrimRight(DestField.AsString);
            end;
          end;
        finally
          FDataSetImpl.RestoreDataSetState(Delta, StateContext);
        end;
      end;
    finally
      IfxSQL.Free;
    end;
  except
    on E: Exception do
    begin
      E.Message := SCannotRefreshRecord + SLineBreak + E.Message;
      raise;
    end;
  end;
end;

{ TIfxCustomDataSet.IProviderSupport }

function TIfxCustomDataSet.PSExecuteStatement(const ASQL: string;
  AParams: TParams; ResultSet: Pointer = nil): Integer;
var
  IfxSQL: TIfxSQL;
  Query: TIfxQuery;
begin
  if ResultSet = nil then
  begin
    IfxSQL := CreateInternalSQL(ASQL);
    try
      IfxSQL.Params.Assign(AParams);
      Result := IfxSQL.Execute;
    finally
      IfxSQL.Free;
    end;
  end else
  begin
    Query := TIfxQuery.Create(nil);
    try
      Query.Connection := Connection;
      Query.FieldTypes := FieldTypes;
      Query.SQLProcessor := ActiveSQLProcessor;
      Query.SQL.Text := ASQL;
      Query.Params.Assign(AParams);
      Query.Open;
      TIfxQuery(ResultSet^) := Query;
      Result := Query.RowsAffected;
    except
      Query.Free;
      raise;
    end;
  end;
end;

function TIfxCustomDataSet.PSGetTableName: string;
begin
  Result := GetTableNameFromSQL(FIfxSQL.SQL.Text)
end;

function TIfxCustomDataSet.PSGetQuoteChar: string;
begin
  Result := '';
end;

function TIfxCustomDataSet.PSGetUpdateException(E: Exception;
  Prev: EUpdateError): EUpdateError;
var
  PrevErr: Integer;
begin
  if Prev <> nil then
    PrevErr := Prev.ErrorCode else
    PrevErr := 0;

  if E is EIfxError then
    Result := EUpdateError.Create(E.Message, '', EIfxError(E).SQL, PrevErr, E) else
    Result := inherited PSGetUpdateException(E, Prev);
end;

function TIfxCustomDataSet.PSIsSQLBased: Boolean;
begin
  Result := True;
end;

function TIfxCustomDataSet.PSIsSQLSupported: Boolean;
begin
  Result := True;
end;

procedure TIfxCustomDataSet.PSReset;
begin
  if Active then Requery;
end;

function TIfxCustomDataSet.PSUpdateRecord(UpdateKind: TUpdateKind;
  Delta: TDataSet): Boolean;
begin
  Result := FDataSetImpl.UpdateRecord(UpdateKind, Delta);
end;

procedure TIfxCustomDataSet.PSStartTransaction;
begin
  CheckConnection.StartTransaction;
end;

procedure TIfxCustomDataSet.PSEndTransaction(Commit: Boolean);
begin
  if Commit then
    CheckConnection.Commit else
    CheckConnection.Rollback;
end;

function TIfxCustomDataSet.PSInTransaction: Boolean;
begin
  Result := CheckConnection.InTransaction;
end;


{ TIfxDataSetSQL }

function TIfxDataSetSQL.DataSet: TIfxCustomDataSet;
begin
  Result := TIfxCustomDataSet(Owner);
end;

function TIfxDataSetSQL.ActiveComponentState: TComponentState;
begin
  Result := DataSet.ComponentState;
end;

procedure TIfxDataSetSQL.RegisterConnection;
begin
  inherited;
  RegisterConnectionClient(DataSet, DataSet.ConnectionStateChange);
end;

procedure TIfxDataSetSQL.UnregisterConnection;
begin
  UnregisterConnectionClient(DataSet);
  inherited;
end;

procedure TIfxDataSetSQL.ConnectionChanging;
begin
  DataSet.CheckInactive;
end;

procedure TIfxDataSetSQL.ConnectionClosing;
begin
  DataSet.Close;
  Unprepare;
end;

procedure TIfxDataSetSQL.BuildText;
begin
  inherited;
  FOriginalText := Text;
  if DataSet.RequestLive then
    SetLiveText;
  if not FIgnoreDataEvent then
    DataSet.DataEvent(dePropertyChange, 0);
end;

procedure TIfxDataSetSQL.InternalUnprepare;
begin
  DataSet.Close;
  inherited;
end;

procedure TIfxDataSetSQL.SetLiveText;
var
  SQL: string;
  Start, P: PChar;
  Token: string;
  SQLToken, CurSection: TSQLToken;
begin
  SQL := FOriginalText;
  Start := PChar(SQL);
  P := Start;
  CurSection := stUnknown;
  repeat
    SQLToken := NextSQLToken(P, Token, CurSection);
    if SQLToken in SQLSections then CurSection := SQLToken;
  until SQLToken in [stEnd, stFrom];
  if SQLToken = stFrom then
    Insert(', rowid ', SQL, (P - Start + 1) - Length(Token));
  SetText(SQL);
end;

procedure TIfxDataSetSQL.UpdateText;
begin
  Unprepare;
  if DataSet.RequestLive then
    SetLiveText else
    SetText(FOriginalText);
end;

function TIfxDataSetSQL.IsLiveText: Boolean;
begin
  Result := FOriginalText <> Text;
end;


end.